<?
require_once "AssertionFailedError.php";

/**
 * A set of assert methods.  Messages are only displayed when an assert fails.
 *
 * This class need to find a way to implement all of the methods that expect Object values
 * as their parameters but yet also need to be able to take a null value (since in Java null
 * is still technically an Object). Need to do this with the minimum possible amount of
 * extra fat.
 *
 * @author Ryan Gantt
 */
class Assert extends Object // this is still coupled to Japha
{
	/**
	 * Protect constructor since it is a static only class
	 */
	public function __construct(){}

	/**
	 * Asserts that a condition is true. If it isn't, it calls Assert::fail which
	 * throws an AssertionFailedError with the given message.
	 *
	 * One args: boolean
	 * Two args: string, boolean
	 *
	 * @see Overloaded methods
	 */
	public static function assertTrue( $condition=null, $message=null )
	{
		if( !$condition )
			self::fail( $message );
	}

	/**
	 * Asserts that a condition is false. If it isn't it throws
	 * an AssertionFailedError with the given message.
	 *
	 * One args: boolean
	 * Two args: string, boolean
	 *
	 * @see Overloaded methods
	 */
	public static function assertFalse( $condition=null, $message=null )
	{
		return self::assertTrue( !$condition, $message );
	}

	/**
	 * Fails a test with the given message.
	 *
	 * @param message The message passed to the AssertionFailedError. This is the message that is displayed on top of the trace
	 * @throws AssertionFailedError Whenever this method is called
	 */
	public static function fail( $message )
	{
		throw new AssertionFailedError( $message );
	}

	/**
	 * Asserts that two objects are equal. If they are not
	 * an AssertionFailedError is thrown with the given message.
	 *
	 * Two args: Object, Object
	 * Three args:
	 *       Arg1 is Object? string, Object, Object
	 *       Arg2 nt Object? string, primitive, primitive
	 *
	 * @see Overloaded methods
	 */
	public static function assertEquals( $first, $second, $message='Condition' )
	{
		if( $first instanceof Object )
		{
			if( !$first->equals( $second ) )
				self::failNotEquals( $message, $first, $second );
		}
		else if( $first !== $second )
				self::failNotEquals( $message, $first, $second );
	}

	/**
	 * Asserts that an object isn't null. If it is
	 * an AssertionFailedError is thrown with the given message.
	 *
	 * @see Overloaded methods
	 */
	public static function assertNotNull( $object=null, $message=null )
	{
		return self::assertTrue( $object !== null, $message );
	}

	/**
	 * Asserts that an object is null.  If it is not
	 * an AssertionFailedError is thrown with the given message.
	 *
	 * @param message The message that will be passed to the AssertionFailedError, and the one that will be shown on top of the trace
	 * @param object The object that may or may not be null
	 */
	public static function assertNull( $message, $object )
	{
		self::assertTrue( $message, $object == null );
	}

	/**
	 * Asserts that two objects refer to the same object. If they are not
	 * an AssertionFailedError is thrown with the given message.
	 *
	 * @param message The message that will be passed to the AssertionFailedError, and the one that will be shown on top of the trace
	 * @param expected The expected value of the "actual" parameter
	 * @param actual The actual value that will be compared to the first
	 */
	public static function assertSame( $message, Object $expected, Object $actual )
	{
		if ( $expected !== $actual )
			self::failNotSame( $message, $expected, $actual );
	}

 	/**
	 * Asserts that two objects refer to the same object. If they are not
	 * an AssertionFailedError is thrown with the given message.
	 *
	 * @param message The message that will be passed to the AssertionFailedError, and the one that will be shown on top of the trace
	 * @param expected A value that the "actual" parameter should NOT be
	 * @param actual The acual value that will be compared to the first
	 */
	public static function assertNotSame( $message, Object $expected, Object $actual )
	{
		if ( $expected === $actual )
			self::failSame( $message );
	}

	/**
	 * Formats an error based on a the message that is passed
	 *
	 * Must figure out a way to ensure that the second and third parameters are Objects, yet also allow them to be
	 * null.
	 *
	 * Both parameters expected and actual should in theory be object, but, since they are sometimes null, we can't
	 * explicity ask for them, unless we want to program for the exception every time.
	 *
	 * @param message The string message to be formatted
	 * @param expected A reference to the expected object
	 * @param actual A reference to the actual object
	 */
	public static function format( $message, $expected, $actual )
	{
		$formatted = "";
		if ( $message != null )
			$formatted .= "{$message} ";
		$formatted .= "expected: &lt;{$expected}&gt; but was:&lt;".( $actual != null ? $actual : 'null' )."&gt;";
		return $formatted;
	}

	/**
	 * Called when an assertion has failed, and the two objects are a reference to the same memory location
	 *
	 * @param message The message that will be formatted, data appended to, and pushed to the top of the stack trace
	 * @throws AssertionFailedError If the called method does
	 */
	private static function failSame( $message )
	{
		$formatted = "";
 		if ( $message != null )
 			$formatted .= "{$message} ";
 		$formatted .= "expected not same";
 		self::fail( $formatted );
	}

	/**
	 * Called when an assertion has failed, and the two objects are not a reference to the same memory location
	 *
	 * Must figure out a way to ensure that the second and third parameters are Objects, yet also allow them to be
	 * null.
	 *
	 * Both parameters expected and actual should in theory be object, but, since they are sometimes null, we can't
	 * explicity ask for them, unless we want to program for the exception every time.
	 *
	 * @param message The message that will be formatted, data appended to, and pushed to the top of the stack trace
	 * @param expected A reference to the expected object
	 * @param actual A reference to the actual object
	 */
	private static function failNotSame( $message, $expected, $actual )
	{
		$formatted = "";
		if ( $message != null )
			$formatted .= "{$message} ";
		$formatted .= "expected same: &lt;{$expected}&gt; was not: &lt;{$actual}&gt;";
		self::fail( $formatted );
	}

	/**
	 * Called when an assertion has failed, and the two objects are not equal in value
	 *
	 * Must figure out a way to ensure that the second and third parameters are Objects, yet also allow them to be
	 * null.
	 *
	 * Both parameters expected and actual should in theory be object, but, since they are sometimes null, we can't
	 * explicity ask for them, unless we want to program for the exception every time.
	 *
	 * @param message The message that will be formatted and then immediately pushed to the top of the stack trace
	 * @param expected A reference to the expected object
	 * @param actual A reference to the actual object
	 */
	private static function failNotEquals( $message, $expected, $actual)
	{
		self::fail( self::format( $message, $expected, $actual ) );
	}
	
	public static function assertException( $eblock )
	{
		try
		{
			eval( $eblock );
		}
		catch( Exception $e )
		{
			return true;
		}
		return false;
	}
}